﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Platformer
{
    public class Character
    {
        // Const
        public const int PLAYER_HEALTH = 200, PLAYER_SPEED = 6, PLAYER_INVINCIBLE_TIME = 60;
        Vector2 PLAYER_POSITION = new Vector2(25, PlatformerGame.HEIGHT * 3/ 4);

        // Variables
        Texture2D texture, layer, shipLayer, strokeLayer, layerSeparator;
        public Texture2D aim;
        PlatformerGame game;
        public static Keys keyJump = Keys.Space, keyRight = Keys.D, keyLeft = Keys.Q, keyJetpack = Keys.Z, keyNextWeap = Keys.O, keyPrevWeap = Keys.P, keyHook = Keys.E;
        KeyboardState KState, previousState;
        int shootTimer, invincibleTimer;
        Vector2 bulletDir;

        // Graphic
        int frameSwitch = 2;
        int timerSinceLastFrame = 0;
        Rectangle frame;
        Point currentFrame = new Point(0, 1);
        int sheetSize;

        // Temp
        float velocity = 1f;
        float normalVelocity = 1f;
        // Jump
        float maxJumpVelocity = 2f;    // UTILISER MEME VARIABLE JUMP/SAUT   POSITIF/NEGATIF
        float maxVelocity = 3f;
        float jetpackVelocityGain = 0.030f;
        // Fall
        float minVelocity = -2f;
        float velocityGain = 0.062f;
        bool canShoot = true;
        Jetpack jetpack;

        // Collision tolerance
        int collTole = 15;
        bool test = false;
        bool canJump = false;

        Hook hookGun;

        public enum Direction
        {
            Right,
            Left
        }
        public Direction CurrentDirection
        {
            get { return currentDir; }
            set { currentDir = value; }
        }
        Direction currentDir = Direction.Right;

        public enum VerticalState
        {
            Idle,
            Flying,
            Falling,
            Jumping
        }
        VerticalState movState = VerticalState.Falling;

        public enum WeaponType
        {
            Gun,
            Minigun,
            Laser,
            Rocket,
            Needler
        }
        WeaponType cureentWeapon = WeaponType.Gun;

        public int Health
        {
            set { health = value; }
            get { return health; }
        }
        int health = PLAYER_HEALTH;

        public int Speed
        {
            set { speed = value; }
            get { return speed; }
        }
        int speed = PLAYER_SPEED;

        // Position of the player sprite
        public Vector2 Position
        {
            set { position = value; }
            get { return position; }
        }
        Vector2 position;

        // Position of the player sprite
        public Vector2 AimPosition
        {
            set { aimPosition = value; }
            get { return aimPosition; }
        }
        Vector2 aimPosition;

        // Size of the player sprite
        public Point Size
        {
            get { return size; }
        }
        Point size;

        // Rect of the player sprite
        public Rectangle Rect
        {
            get { return rect; }
        }
        Rectangle rect;

        // Lifebar of the player
        public LifeBar LifeBare
        {
            set { lifeBar = value; }
            get { return lifeBar; }
        }
        LifeBar lifeBar;

        // Can the player take damages ?
        public bool Invincible
        {
            set { invincible = value; }
            get { return invincible; }
        }
        bool invincible = false;

        // End of the game
        public bool GameOver
        {
            set { gameOver = value; }
            get { return gameOver; }
        }
        bool gameOver = false;

        // Weapon of the player
        public Weapon Weapon
        {
            set { weapon = value; }
            get { return weapon; }
        }
        Weapon weapon;

        // Weapon of the player
        public List<Bullet> BulletList
        {
            set { bulletList = value; }
            get { return bulletList; }
        }
        List<Bullet> bulletList = new List<Bullet>();

        /// <summary>
        /// The character the player will be using.
        /// </summary>
        /// <param name="game">The current Platformer game.</param>
        public Character(PlatformerGame game)
        {
            this.game = game;
            Initialize();
            SetProperties();
        }

        /// <summary>
        /// Update method of the class.
        /// </summary>
        public void Update()
        {
            previousState = KState;
            KState = Keyboard.GetState();
            AimHandler();
            jetpack.Update();
            weapon.Update();
            hookGun.Update();

            foreach (Bullet item in bulletList)
                item.Update();

            for (int i = 0; i < bulletList.Count; i++)
            {
                if (bulletList[i].Wasted)
                    bulletList.Remove(bulletList[i]);
            }

            UpdateFrame();
            Shoot();
            Input();
            HandleMovement();
            CollisionWithBlocks();
        }

        /// <summary>
        /// Draw method of the class.
        /// </summary>
        /// <param name="spriteBatch"></param>
        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(texture, position, frame, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
            spriteBatch.Draw(aim, aimPosition, Color.Red);

            weapon.Draw(spriteBatch);
            foreach (Bullet item in bulletList)
                item.Draw(spriteBatch);

            spriteBatch.DrawString(PlatformerGame.debugFont, "Aim position : " + aimPosition, new Vector2(0, 0), Color.Red);
            spriteBatch.DrawString(PlatformerGame.debugFont, "Player positio : " + position, new Vector2(0, 25), Color.Red);

            spriteBatch.DrawString(PlatformerGame.debugFont, "X : " + currentFrame.X, new Vector2(0, 75), Color.Red);
            spriteBatch.DrawString(PlatformerGame.debugFont, "Y : " + currentFrame.Y, new Vector2(0, 100), Color.Red);

            spriteBatch.DrawString(PlatformerGame.debugFont, "velocity : " + velocity, new Vector2(0, 125), Color.Red);
            spriteBatch.DrawString(PlatformerGame.debugFont, "State : " + movState, new Vector2(0, 150), Color.Red);

            spriteBatch.DrawString(PlatformerGame.debugFont, "Mouse  : " + Mouse.GetState().ScrollWheelValue, new Vector2(0, 175), Color.Red);

            spriteBatch.DrawString(PlatformerGame.debugFont, "Weapon  : " + weapon.ToString(), new Vector2(0, 200), Color.Red);
            spriteBatch.DrawString(PlatformerGame.debugFont, "Bullet Count  : " + bulletList.Count, new Vector2(0, 225), Color.Red);

            spriteBatch.DrawString(PlatformerGame.debugFont, "Test  : " + test, new Vector2(0, 250), Color.Red);

            spriteBatch.DrawString(PlatformerGame.debugFont, "HookActiv  : " + hookGun.activated, new Vector2(700, 0), Color.Red);
            spriteBatch.DrawString(PlatformerGame.debugFont, "HookNumb  : " + hookGun.hookPieceList.Count, new Vector2(700, 25), Color.Red);
            spriteBatch.DrawString(PlatformerGame.debugFont, "HookSpawn  : " + hookGun.spawnPosition, new Vector2(700, 50), Color.Red);

            jetpack.Draw(spriteBatch);

            hookGun.Draw(spriteBatch);
        }

        /* Custom */

        /// <summary>
        /// Retrives every input of the player, and uses it to call the matching functions
        /// </summary>
        void Input()
        {
            // Idle
            if (KState.IsKeyUp(keyLeft) && KState.IsKeyUp(keyRight) && movState != VerticalState.Jumping
                    || KState.IsKeyDown(keyLeft) && KState.IsKeyDown(keyRight))
            {
                frameSwitch = 15;
                if(currentDir == Direction.Left)
                    currentFrame.Y = 0;
                else
                    currentFrame.Y = 1;
            }
            // Right movement
            else if (KState.IsKeyDown(keyRight))
            {
                frameSwitch = 2;
                Movement(1);
                currentFrame.Y = 3;
                currentDir = Direction.Right;
            }
            // Left movement
            else if (KState.IsKeyDown(keyLeft))
            {
                frameSwitch = 2;
                Movement(-1);
                currentFrame.Y = 2;
                currentDir = Direction.Left;
            }

            // Jump
            if (KState.IsKeyDown(keyJump) && canJump)
            {
                velocity = maxJumpVelocity;
                movState = VerticalState.Jumping;
                canJump = false;
            }


            // Weapon swap
            if (KState.IsKeyDown(keyNextWeap) && previousState.IsKeyUp(keyNextWeap))
            {
                ChangeWeapon(WeaponType.Minigun);
            }
            else if (KState.IsKeyDown(keyPrevWeap) && previousState.IsKeyUp(keyPrevWeap))
            {
                ChangeWeapon(WeaponType.Gun);
            }
            
        }

        /// <summary>
        /// Allows to move the character or to simulate his movement by moving the background elements.
        /// </summary>
        /// <param name="dir">Horizontal direction of the movement (right : 1, left : -1).</param>
        void Movement(sbyte dir)
        {
            // Right movement
            if (dir == 1)
            {
                position.X += speed;
            }
            // Left movement
            else if(dir == -1)
            {
                position.X -= speed;
            }
            ScreenBounds();
        }

        void ScreenBounds()
        {
            if (position.X < 0)
            {
                position.X = 0;
            }

            //TODO
        }

        /// <summary>
        /// Handles all possible movement types of the character.
        /// </summary>
        void HandleMovement()
        {
            // If the player is not on the floor, update his Y position
            position.Y -= velocity * speed;
            if (movState == VerticalState.Idle)
                velocity = 0f;


            if (position.Y + size.Y >= PlatformerGame.HEIGHT)
            {
                ResetVelocity();
                position.Y = PlatformerGame.HEIGHT - size.Y;
                movState = VerticalState.Idle;
            }

            if (KState.IsKeyDown(keyJetpack))
            {
                // Avoid using jump velocity to get higher velocity than 1
                if (velocity > 1f)
                    velocity -= jetpackVelocityGain;
                if(velocity < 1f)
                    velocity += jetpackVelocityGain;
                movState = VerticalState.Flying;
                return;
            }
            else if (KState.IsKeyUp(keyJetpack) && movState == VerticalState.Flying)
            {
                movState = VerticalState.Falling;
            }
            // Gravity
            else if (movState == VerticalState.Falling)
            {
                if (velocity > -2f)
                    velocity -= velocityGain;
            }
            // Jump
            else if (movState == VerticalState.Jumping)
            {
                if (velocity < 0)
                {
                    movState = VerticalState.Falling;
                    return;
                }
                velocity -= velocityGain;
            }
        }

        /// <summary>
        /// Shoot a bullet with the current weapon.
        /// </summary>
        void Shoot()
        {
            shootTimer++;          
            if (Mouse.GetState().LeftButton == ButtonState.Pressed && shootTimer > weapon.FireRate && canShoot)
            {
                shootTimer = 0;
                weapon.Fire();
            }
        }

        /// <summary>
        /// Handles the graphical part of the character.
        /// </summary>
        void UpdateFrame()
        {
            if (currentFrame.Y == 0 || currentFrame.Y == 1)
                sheetSize = 11;
            else if (currentFrame.Y == 2 || currentFrame.Y == 3)
                sheetSize = 8;

            timerSinceLastFrame++;
            if (timerSinceLastFrame > frameSwitch)
            {
                timerSinceLastFrame = 0;

                if (currentFrame.X >= sheetSize - 1)
                    currentFrame.X = 0;
                else
                    currentFrame.X++;

                frame = new Rectangle(currentFrame.X * size.X, currentFrame.Y * size.Y, size.X, size.Y);
            }
        }

        void ResetVelocity()
        {
            velocity = 0f;
        }

        void Initialize()
        {
            texture = game.Content.Load<Texture2D>("Character/Sprites/character");
            aim = game.Content.Load<Texture2D>("Character/Weapons/aim");
        }
        
        /// <summary>
        /// Set all character properties to the default ones.
        /// </summary>
        void SetProperties()
        {
            weapon = new Minigun(game);
            jetpack = new Jetpack(game);
            hookGun = new Hook(game);
            health = PLAYER_HEALTH;
            speed = PLAYER_SPEED;
            position = PLAYER_POSITION;
            size = new Point(56, 77);
        }

        /// <summary>
        /// Handles the aim of the player.
        /// </summary>
        void AimHandler()
        {
            aimPosition = new Vector2(Mouse.GetState().X - aim.Width / 2, Mouse.GetState().Y - aim.Height / 2);

            if (movState == VerticalState.Idle)
            {
                canShoot = true;
                return;
            }
            if (currentDir == Direction.Right)
            {
                if (aimPosition.X + aim.Width / 2 < position.X + size.X / 2)
                    canShoot = false;
                else canShoot = true;
            }
            else
            {
                if (aimPosition.X + aim.Width / 2 > position.X + size.X / 2)
                    canShoot = false;
                else canShoot = true;
            }
        }

        void CollisionWithBlocks()
        {
            foreach (Block item in game.BlocksList)
            {
                // Bot (of the character) collision
                if (position.Y + size.Y > item.Position.Y && position.Y + size.Y < item.Position.Y + Block.Size.Y / 2
                         && position.X + size.X - collTole > item.Position.X && position.X + collTole < item.Position.X + Block.Size.X)
                {
                    position.Y = item.Position.Y - size.Y;
                    movState = VerticalState.Idle;
                    velocity = 0f;
                    canJump = true;
                }
                // Top (of the character) collision
                else if (position.Y < item.Position.Y + Block.Size.Y && position.Y > item.Position.Y + Block.Size.Y / 2
                    && position.X + size.X - collTole > item.Position.X && position.X + collTole < item.Position.X + Block.Size.X)
                {
                    if (movState == VerticalState.Jumping)
                    {
                        movState = VerticalState.Falling;
                        velocity = 0f;
                        return;
                    }
                    position.Y = item.Position.Y + Block.Size.Y;
                }
                // Right (of the character) collision
                else if (position.Y + size.Y - collTole > item.Position.Y && position.Y + collTole < item.Position.Y + Block.Size.Y / 2
                 && position.X + size.X > item.Position.X && position.X + size.X < item.Position.X + Block.Size.X)
                {
                    position.X = item.Position.X - size.X;
                    test = true;
                }
                // Left (of the character) collision
                else if (position.Y + size.Y - collTole > item.Position.Y && position.Y + collTole < item.Position.Y + Block.Size.Y / 2
                 && position.X < item.Position.X + Block.Size.X && position.X > item.Position.X + Block.Size.X / 2)
                {
                    position.X = item.Position.X + Block.Size.X;
                }
                else if(movState != VerticalState.Jumping && movState != VerticalState.Flying)
                    movState = VerticalState.Falling;
            }
        }

        /// <summary>
        /// Change the current weapon of the character.
        /// </summary>
        /// <param name="wt">Type of the weapon.</param>
        void ChangeWeapon(WeaponType wt)
        {
            switch (wt)
            {
                case WeaponType.Gun:
                    weapon = new Gun(game);
                    break;
                case WeaponType.Minigun:
                    weapon = new Minigun(game);
                    break;
                case WeaponType.Laser:
                    weapon = new Gun(game);
                    break;
                case WeaponType.Rocket:
                    weapon = new Gun(game);
                    break;
                default:
                    break;
            }
        }

    }

    public class LifeBar
    {

        // Use draw with rectangle

        public LifeBar()
        {

        }
    }
}
